Программирование сетевых приложений

Основы QT

Программирование сетевых приложений

Содержание лекции

  • Введение в Qt
  • История и развитие Qt
  • Архитектура и основные принципы Qt
  • Модульная структура Qt
  • Метаобъектная система (MOC)
  • Сигналы и слоты
  • Событийная система Qt
  • Управление памятью
  • Инструменты разработки Qt
  • Qt Network — TCP-сервер и UDP-сокеты
  • HTTP-запросы и QNetworkAccessManager
  • Многопоточность и сетевые операции
  • SSL/TLS в Qt Network
  • Преимущества и недостатки Qt
Основы QT
Программирование сетевых приложений

Введение в Qt

Что такое Qt?

Qt - это кроссплатформенный фреймворк для разработки приложений с графическим интерфейсом

Основные характеристики

  • Кроссплатформенность - Windows, macOS, Linux, Android, iOS
  • Богатая функциональность - GUI, сети, базы данных, мультимедиа
  • Высокая производительность - нативные приложения
  • Обширная документация и сообщество
  • Коммерческая и open source лицензии

Области применения

  • Десктопные приложения - офисные программы, IDE
  • Мобильные приложения - Android и iOS
  • Встроенные системы - IoT устройства
  • Автомобильная промышленность - приборные панели
  • Промышленная автоматизация - SCADA системы
Основы QT
Программирование сетевых приложений

История и развитие Qt

Ключевые этапы развития

  • 1995 год - Qt 1.0, основание Trolltech (Ховард Норд и Айрик Чамб-Энг)
  • 1999 год - Qt 2.0 с поддержкой UNIX и Windows
  • 2001 год - Qt 3.0, расширение платформ
  • 2005 год - Qt 4.0 с новой модульной архитектурой
  • 2008 год - приобретение Nokia
  • 2010 год - Qt 4.7, появление Qt Quick и QML
  • 2012 год - Qt 5.0 с QML как основой UI
  • 2014 год - создание The Qt Company после выделения из Digia
  • 2020 год - Qt 6.0 с улучшенной производительностью и C++17

Эволюция подходов

  • Qt Widgets - классический подход к GUI
  • Qt Quick - современный декларативный UI
  • QML - язык описания интерфейса
  • Qt 3D - 3D графика и анимации
  • Qt WebEngine - встроенный браузерный движок
Основы QT
Программирование сетевых приложений

Архитектура и основные принципы Qt

Архитектурные принципы

  • Модульность - независимые компоненты
  • Переносимость - единый код для всех платформ
  • Расширяемость - возможность создания собственных компонентов
  • Производительность - нативная компиляция
  • Обратная совместимость - поддержка старых версий

Основные концепции

  • Объектная модель - наследование от QObject
  • Событийная система - обработка событий
  • Сигналы и слоты - механизм взаимодействия
  • Метаобъектная система - рефлексия в C++
  • Управление памятью - автоматическое управление ресурсами
Основы QT
Программирование сетевых приложений

Модульная структура Qt

Основные модули Qt6

Модуль Назначение Примеры использования
Qt Core Базовые классы и функциональность QObject, QVariant, контейнеры
Qt GUI Базовые классы GUI QWidget, QApplication, события
Qt Widgets Классические элементы интерфейса QPushButton, QLineEdit, QTableWidget
Qt Quick Современный декларативный UI QML, анимации, сенсорный ввод
Qt Network Сетевое программирование QTcpSocket, QUdpSocket, HTTP
Qt SQL Работа с базами данных QSqlDatabase, QSqlQuery
Qt Multimedia Аудио, видео, камера QMediaPlayer, QCamera
Qt WebEngine Веб-браузер и движок WebView, JavaScript интеграция
Основы QT
Программирование сетевых приложений

Метаобъектная система (MOC)

Что такое MOC?

Meta-Object Compiler (MOC) - препроцессор, генерирующий дополнительный код для поддержки метаобъектной системы

Возможности MOC

  • Сигналы и слоты - механизм событий
  • Информация о типах - RTTI (Runtime Type Information)
  • Свойства объектов - динамические свойства
  • Трансляция строк - поддержка интернационализации
Основы QT
Программирование сетевых приложений

Пример использования MOC

#include <QObject>
#include <QDebug>

class NetworkManager : public QObject {
    Q_OBJECT  // Макрос для MOC
    
    Q_PROPERTY(QString status READ getStatus WRITE setStatus NOTIFY statusChanged)
    Q_PROPERTY(int connectionCount READ getConnectionCount)
    
public:
    explicit NetworkManager(QObject* parent = nullptr) 
        : QObject(parent), m_status("Disconnected"), m_connectionCount(0) {}
    
    QString getStatus() const { return m_status; }
    void setStatus(const QString& status) {
        if (m_status != status) {
            m_status = status;
            emit statusChanged(status);
        }
    }
    
    int getConnectionCount() const { return m_connectionCount; }
    
signals:  // Сигналы генерируются MOC
    void statusChanged(const QString& newStatus);
    void connectionEstablished(const QString& address);
    void connectionLost(const QString& address);
    
public slots:  // Слоты генерируются MOC
    void connectToServer(const QString& address, int port) {
        qDebug() << "Подключение к серверу:" << address << ":" << port;
        setStatus("Connecting...");
        // Логика подключения
    }
    
    void disconnectFromServer() {
        qDebug() << "Отключение от сервера";
        setStatus("Disconnected");
    }
    
private:
    QString m_status;
    int m_connectionCount;
};
Основы QT
Программирование сетевых приложений

Сигналы и слоты

Основные принципы

  • Сигналы - уведомления о событиях
  • Слоты - обработчики событий
  • Соединения - связывание сигналов со слотами
  • Автоматическое управление - нет необходимости в ручном вызове
Основы QT
Программирование сетевых приложений

Типы соединений

#include <QObject>
#include <QTimer>
#include <QDebug>

class ConnectionExample : public QObject {
    Q_OBJECT
    
public:
    ConnectionExample(QObject* parent = nullptr) : QObject(parent) {
        // 1. Прямое соединение (по умолчанию)
        connect(&timer, &QTimer::timeout, this, &ConnectionExample::onTimeout);
        
        // 2. Соединение через очередь (для разных потоков)
        connect(&timer, &QTimer::timeout, this, &ConnectionExample::onTimeout, Qt::QueuedConnection);
        
        // 3. Соединение с контекстом (автоотключение при разрушении контекста)
        
        // 4. Лямбда-функции
        connect(&timer, &QTimer::timeout, []() {
            qDebug() << "Таймер сработал!";
        });
    }
    
public slots:
    void onTimeout() {
        qDebug() << "Обработка таймера";
    }
    
signals:
    void dataReceived(const QByteArray& data);
    void connectionStateChanged(bool connected);
    
private:
    QTimer timer;
};
Основы QT
Программирование сетевых приложений

Событийная система Qt

Иерархия событий

  • QEvent - базовый класс всех событий
  • QMouseEvent - события мыши
  • QKeyEvent - события клавиатуры
  • QResizeEvent - изменение размера
  • QPaintEvent - перерисовка
  • QCloseEvent - закрытие окна
Основы QT
Программирование сетевых приложений

Обработка событий

#include <QWidget>
#include <QMouseEvent>
#include <QKeyEvent>
#include <QPaintEvent>
#include <QDebug>

class CustomWidget : public QWidget {
    Q_OBJECT
    
public:
    CustomWidget(QWidget* parent = nullptr) : QWidget(parent) {
        setMouseTracking(true);  // Отслеживание мыши без нажатия
    }
    
protected:
    // События мыши
    void mousePressEvent(QMouseEvent* event) override {
        qDebug() << "Нажата кнопка мыши:" << event->button() 
                 << "в позиции:" << event->pos();
        
        if (event->button() == Qt::LeftButton) {
            // Логика для левой кнопки
            update();  // Запрос перерисовки
        }
    }
    
    void mouseMoveEvent(QMouseEvent* event) override {
        qDebug() << "Перемещение мыши:" << event->pos();
    }
    
    void mouseReleaseEvent(QMouseEvent* event) override {
        qDebug() << "Отпущена кнопка мыши:" << event->button();
    }
    
    // События клавиатуры
    void keyPressEvent(QKeyEvent* event) override {
        qDebug() << "Нажата клавиша:" << event->key() 
                 << "текст:" << event->text();
        
        if (event->key() == Qt::Key_Escape) {
            close();  // Закрыть окно по Escape
        }
    }
    
    // Событие перерисовки
    void paintEvent(QPaintEvent* event) override {
        QPainter painter(this);
        painter.fillRect(rect(), Qt::blue);
        painter.drawText(rect(), Qt::AlignCenter, "Кастомный виджет");
    }
    
    // Событие изменения размера
    void resizeEvent(QResizeEvent* event) override {
        qDebug() << "Размер изменен:" << event->oldSize() 
                 << "->" << event->size();
    }
};
Основы QT
Программирование сетевых приложений

Управление памятью

Родительско-дочерние отношения

#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>

class MemoryManagementExample : public QWidget {
    Q_OBJECT
    
public:
    MemoryManagementExample(QWidget* parent = nullptr) 
        : QWidget(parent) {
        
        // Создание компоновщика (родитель)
        QVBoxLayout* layout = new QVBoxLayout(this);
        
        // Создание виджетов с указанием родителя
        QLabel* label = new QLabel("Пример управления памятью", this);
        QPushButton* button = new QPushButton("Нажми меня", this);
        
        // Добавление в компоновщик
        layout->addWidget(label);
        layout->addWidget(button);
        
        // Автоматическое удаление при закрытии родителя
        // Все дочерние объекты будут удалены автоматически
    }
    
    // Не нужно вручную удалять дочерние объекты!
    // Qt автоматически освободит память
};
Основы QT
Программирование сетевых приложений

Умные указатели в Qt

#include <QObject>
#include <QSharedPointer>
#include <QWeakPointer>

class SmartPointerExample : public QObject {
    Q_OBJECT
    
public:
    void demonstrateSmartPointers() {
        // QSharedPointer - совместное владение
        QSharedPointer<QObject> sharedObj(new QObject());
        QSharedPointer<QObject> sharedObj2 = sharedObj;
        
        // QWeakPointer - слабая ссылка
        QWeakPointer<QObject> weakObj = sharedObj;
        
        // Проверка валидности
        if (!weakObj.isNull()) {
            QSharedPointer<QObject> strongRef = weakObj.toStrongRef();
            // Использование объекта
        }
        
        // Автоматическое удаление когда все ссылки исчезнут
    }
};
Основы QT
Программирование сетевых приложений

Инструменты разработки Qt

Qt Creator

  • IDE для разработки Qt приложений
  • Визуальный дизайнер интерфейсов
  • Отладчик и профилировщик
  • Поддержка QML и C++
Основы QT
Программирование сетевых приложений

Qt Designer

// Создание интерфейса программно
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QLabel>

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    QWidget window;
    window.setWindowTitle("Пример Qt приложения");
    window.resize(400, 300);
    
    // Создание элементов интерфейса
    QVBoxLayout* mainLayout = new QVBoxLayout;
    
    // Поле ввода
    QHBoxLayout* inputLayout = new QHBoxLayout;
    QLabel* nameLabel = new QLabel("Имя:");
    QLineEdit* nameEdit = new QLineEdit;
    inputLayout->addWidget(nameLabel);
    inputLayout->addWidget(nameEdit);
    
    // Кнопки
    QHBoxLayout* buttonLayout = new QHBoxLayout;
    QPushButton* okButton = new QPushButton("OK");
    QPushButton* cancelButton = new QPushButton("Отмена");
    buttonLayout->addWidget(okButton);
    buttonLayout->addWidget(cancelButton);
    
    // Сборка интерфейса
    mainLayout->addLayout(inputLayout);
    mainLayout->addLayout(buttonLayout);
    
    window.setLayout(mainLayout);
    window.show();
    
    return app.exec();
}
Основы QT
Программирование сетевых приложений

qmake и CMake

# CMakeLists.txt для Qt проекта
cmake_minimum_required(VERSION 3.16)
project(QtExample)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Поиск Qt6
find_package(Qt6 COMPONENTS Core Widgets REQUIRED)

# Создание исполняемого файла
add_executable(QtExample main.cpp)

# Связывание с Qt модулями
target_link_libraries(QtExample PRIVATE Qt6::Core Qt6::Widgets)

# Включение автоматической генерации MOC
set_target_properties(QtExample PROPERTIES
    AUTOMOC ON
    AUTOUIC ON
    AUTORCC ON
)
Основы QT
Программирование сетевых приложений

Преимущества Qt

Технические преимущества

  • Кроссплатформенность - один код для всех платформ
  • Производительность - нативная компиляция
  • Богатая функциональность - готовые компоненты
  • Зрелость - более 30 лет развития
  • Документация - подробная документация и примеры

Разработческие преимущества

  • Быстрая разработка - готовые компоненты
  • Визуальное проектирование - Qt Designer
  • Сообщество - большое сообщество разработчиков
  • Поддержка - коммерческая и техническая поддержка
Основы QT
Программирование сетевых приложений

Недостатки Qt

Технические ограничения

  • Размер исполняемых файлов - большие бинарники
  • Зависимости - требуется Qt библиотеки
  • Производительность QML - может быть медленнее нативного кода
  • Лицензирование - коммерческая лицензия для некоторых случаев

Разработческие ограничения

  • Изучение - требует времени для освоения
  • Специфичность - нестандартный подход к программированию
  • Экосистема - меньше библиотек по сравнению с другими платформами
Основы QT
Программирование сетевых приложений

Сравнение с альтернативами

Qt vs другие фреймворки

Критерий Qt WPF JavaFX Electron
Кроссплатформенность Отличная Windows Хорошая Отличная
Производительность Высокая Высокая Средняя Низкая
Размер приложения Средний Средний Большой Очень большой
Нативность Полная Полная Частичная Веб-технологии
Сообщество Большое Среднее Маленькое Большое
Лицензия LGPL/Commercial MIT (.NET) GPL/Commercial MIT
Основы QT
Программирование сетевых приложений

Практические примеры использования

Сетевое приложение на Qt

#include <QApplication>
#include <QTcpSocket>
#include <QTextEdit>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QWidget>

class NetworkClient : public QWidget {
    Q_OBJECT
    
public:
    NetworkClient(QWidget* parent = nullptr) : QWidget(parent) {
        setupUI();
        setupConnections();
    }
    
private slots:
    void connectToServer() {
        socket->connectToHost(serverEdit->text(), portEdit->text().toInt());
    }
    
    void onConnected() {
        logEdit->append("Подключен к серверу");
    }
    
    void onDisconnected() {
        logEdit->append("Отключен от сервера");
    }
    
    void onReadyRead() {
        QByteArray data = socket->readAll();
        logEdit->append("Получено: " + data);
    }
    
    void sendMessage() {
        if (socket->state() == QAbstractSocket::ConnectedState) {
            socket->write(messageEdit->text().toUtf8());
            messageEdit->clear();
        }
    }
Основы QT
Программирование сетевых приложений
private:
    void setupUI() {
        QVBoxLayout* layout = new QVBoxLayout;
        
        // Поля подключения
        QHBoxLayout* connectionLayout = new QHBoxLayout;
        serverEdit = new QLineEdit("localhost");
        portEdit = new QLineEdit("1234");
        connectButton = new QPushButton("Подключиться");
        connectionLayout->addWidget(new QLabel("Сервер:"));
        connectionLayout->addWidget(serverEdit);
        connectionLayout->addWidget(new QLabel("Порт:"));
        connectionLayout->addWidget(portEdit);
        connectionLayout->addWidget(connectButton);
        
        // Поле сообщения
        QHBoxLayout* messageLayout = new QHBoxLayout;
        messageEdit = new QLineEdit;
        sendButton = new QPushButton("Отправить");
        messageLayout->addWidget(messageEdit);
        messageLayout->addWidget(sendButton);
        
        // Лог
        logEdit = new QTextEdit;
        logEdit->setReadOnly(true);
        
        layout->addLayout(connectionLayout);
        layout->addLayout(messageLayout);
        layout->addWidget(logEdit);
        
        setLayout(layout);
        setWindowTitle("Qt Network Client");
        resize(600, 400);
    }
Основы QT
Программирование сетевых приложений
    void setupConnections() {
        socket = new QTcpSocket(this);
        
        connect(connectButton, &QPushButton::clicked, this, &NetworkClient::connectToServer);
        connect(sendButton, &QPushButton::clicked, this, &NetworkClient::sendMessage);
        connect(socket, &QTcpSocket::connected, this, &NetworkClient::onConnected);
        connect(socket, &QTcpSocket::disconnected, this, &NetworkClient::onDisconnected);
        connect(socket, &QTcpSocket::readyRead, this, &NetworkClient::onReadyRead);
    }
    
    QTcpSocket* socket;
    QLineEdit* serverEdit;
    QLineEdit* portEdit;
    QLineEdit* messageEdit;
    QPushButton* connectButton;
    QPushButton* sendButton;
    QTextEdit* logEdit;
};

// Файл main.cpp — отдельная единица трансляции
// Класс NetworkManager должен быть в отдельном .h/.cpp файле
// CMake с AUTOMOC ON автоматически обработает Q_OBJECT

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    
    NetworkClient client;
    client.show();
    
    return app.exec();
}
Основы QT
Программирование сетевых приложений

Qt Network — обзор возможностей

Классы для сетевого программирования

Класс Назначение Протокол
QTcpSocket TCP-клиент TCP
QTcpServer TCP-сервер TCP
QUdpSocket Дейтаграммная передача UDP
QNetworkAccessManager HTTP/HTTPS запросы HTTP(S)
QNetworkReply Ответ HTTP-запроса HTTP(S)
QSslSocket Зашифрованное соединение TLS/SSL
QWebSocket Полнодуплексный канал WebSocket
QHostInfo DNS-разрешение имён DNS
QNetworkInterface Информация о сетевых интерфейсах
Основы QT
Программирование сетевых приложений

TCP-сервер на Qt

Пример эхо-сервера

#include <QTcpServer>
#include <QTcpSocket>
#include <QDateTime>

class EchoServer : public QObject {
    Q_OBJECT
public:
    explicit EchoServer(quint16 port, QObject* parent = nullptr)
        : QObject(parent) {
        server = new QTcpServer(this);
        connect(server, &QTcpServer::newConnection, this, &EchoServer::onNewConnection);
        
        if (server->listen(QHostAddress::Any, port)) {
            qDebug() << "Сервер запущен на порту" << port;
        } else {
            qDebug() << "Ошибка запуска:" << server->errorString();
        }
    }
    
private slots:
    void onNewConnection() {
        QTcpSocket* client = server->nextPendingConnection();
        qDebug() << "Новое подключение:" << client->peerAddress().toString();
        
        connect(client, &QTcpSocket::readyRead, [client]() {
            QByteArray data = client->readAll();
            qDebug() << "Получено:" << data;
            client->write("Echo: " + data);
        });
        
        connect(client, &QTcpSocket::disconnected, client, &QTcpSocket::deleteLater);
    }
    
private:
    QTcpSocket* server;
};

Примечание: deleteLater гарантирует безопасное удаление сокета после завершения обработки текущего события.

Основы QT
Программирование сетевых приложений

UDP-сокеты в Qt

Дейтаграммная передача

#include <QUdpSocket>
#include <QNetworkDatagram>

class UdpChat : public QObject {
    Q_OBJECT
public:
    UdpChat(quint16 port, QObject* parent = nullptr) : QObject(parent) {
        socket = new QUdpSocket(this);
        socket->bind(QHostAddress::Any, port, QAbstractSocket::ShareAddress);
        
        connect(socket, &QUdpSocket::readyRead, this, &UdpChat::onReadyRead);
    }
    
    void sendMessage(const QString& message, const QHostAddress& address, quint16 port) {
        QByteArray data = message.toUtf8();
        socket->writeDatagram(data, address, port);
    }
    
private slots:
    void onReadyRead() {
        while (socket->hasPendingDatagrams()) {
            QNetworkDatagram datagram = socket->receiveDatagram();
            qDebug() << "От:" << datagram.senderAddress() << datagram.senderPort()
                     << "Сообщение:" << datagram.data();
        }
    }
    
private:
    QUdpSocket* socket;
};
Основы QT
Программирование сетевых приложений

HTTP-запросы с QNetworkAccessManager

Асинхронные HTTP-запросы

#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>

class HttpClient : public QObject {
    Q_OBJECT
public:
    explicit HttpClient(QObject* parent = nullptr) : QObject(parent) {
        manager = new QNetworkAccessManager(this);
        connect(manager, &QNetworkAccessManager::finished,
                this, &HttpClient::onReplyFinished);
    }
    
    void getRequest(const QUrl& url) {
        QNetworkRequest request(url);
        request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
        manager->get(request);
    }
    
    void postRequest(const QUrl& url, const QJsonObject& data) {
        QNetworkRequest request(url);
        request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
        manager->post(request, QJsonDocument(data).toJson());
    }
    
private slots:
    void onReplyFinished(QNetworkReply* reply) {
        if (reply->error() == QNetworkReply::NoError) {
            QByteArray response = reply->readAll();
            QJsonDocument doc = QJsonDocument::fromJson(response);
            qDebug() << "Ответ:" << doc.toJson();
        } else {
            qDebug() << "Ошибка HTTP:" << reply->errorString();
        }
        reply->deleteLater();
    }
    
private:
    QNetworkAccessManager* manager;
};
Основы QT
Программирование сетевых приложений

Многопоточность и сеть

QThread + сетевые операции

#include <QThread>
#include <QTcpSocket>
#include <QMutex>

class NetworkWorker : public QObject {
    Q_OBJECT
public slots:
    void doConnect(const QString& host, quint16 port) {
        QTcpSocket socket;
        socket.connectToHost(host, port);
        if (socket.waitForConnected(5000)) {
            emit connected();
            socket.write("Hello from thread");
            socket.waitForBytesWritten();
        } else {
            emit errorOccurred(socket.errorString());
        }
    }
    
    void doLongPolling(const QString& host, quint16 port) {
        QTcpSocket socket;
        socket.connectToHost(host, port);
        if (!socket.waitForConnected(5000)) return;
        
        while (!QThread::currentThread()->isInterruptionRequested()) {
            if (socket.waitForReadyRead(1000)) {
                emit dataReceived(socket.readAll());
            }
        }
    }
    
signals:
    void connected();
    void dataReceived(const QByteArray& data);
    void errorOccurred(const QString& error);
};

// Использование в main():
// QThread* thread = new QThread;
// NetworkWorker* worker = new NetworkWorker;
// worker->moveToThread(thread);
// connect(thread, &QThread::started, worker, [worker]() {
//     worker->doConnect("example.com", 80);
// });
// connect(worker, &NetworkWorker::dataReceived, [](const QByteArray& data) {
//     qDebug() << "Данные из потока:" << data;
// });
// thread->start();

Важно: QTcpSocket нельзя использовать из потока, отличного от того, в котором он был создан. Создавайте сокет внутри doConnect().

Основы QT
Программирование сетевых приложений

SSL/TLS в Qt Network

Защищённые соединения

#include <QSslSocket>
#include <QSslConfiguration>

class SecureClient : public QObject {
    Q_OBJECT
public:
    void connectSecure(const QString& host, quint16 port) {
        QSslSocket* socket = new QSslSocket(this);
        
        connect(socket, &QSslSocket::encrypted, [socket]() {
            qDebug() << "SSL-соединение установлено с"
                     << socket->peerCertificate().subjectInfo(QSslCertificate::CommonName);
        });
        
        connect(socket, &QSslSocket::sslErrors, [](const QList<QSslError>& errors) {
            for (const auto& err : errors)
                qDebug() << "SSL ошибка:" << err.errorString();
        });
        
        socket->connectToHostEncrypted(host, port);
    }
};
Основы QT
Программирование сетевых приложений

Заключение

Ключевые преимущества Qt

  • Универсальность - подходит для различных типов приложений
  • Производительность - нативная компиляция и оптимизация
  • Сообщество - активная поддержка и развитие
  • Инструменты - богатая экосистема разработки

Области применения в сетевом программировании

  • Клиент-серверные приложения - богатые GUI клиенты
  • Сетевые утилиты - мониторинг, анализ трафика
  • Встроенные системы - IoT устройства с GUI
  • Промышленные приложения - SCADA, HMI системы

Перспективы развития

  • Qt 6 и далее - улучшенная производительность
  • QML и Qt Quick - современные подходы к UI
  • Интеграция с веб-технологиями - гибридные приложения
  • Мобильная разработка - кроссплатформенные мобильные приложения
Основы QT
Программирование сетевых приложений

Вопросы для самопроверки

  1. Что такое MOC и какие возможности он предоставляет?
  2. Как работает механизм сигналов и слотов в Qt?
  3. Какие основные модули входят в состав Qt6?
  4. Как происходит управление памятью в Qt?
  5. В чём преимущества кроссплатформенности Qt?
  6. Какие типы событий поддерживает Qt?
  7. Как создать простое GUI приложение на Qt?
  8. Какие инструменты разработки предоставляет Qt?
  9. В чём отличие Qt Widgets от Qt Quick?
  10. Как создать TCP-сервер на Qt? Объясните роль QTcpServer::newConnection.
  11. Чем отличается QUdpSocket от QTcpSocket? Когда лучше использовать UDP?
  12. Как выполнить HTTP-запрос с помощью QNetworkAccessManager?
  13. Почему QTcpSocket нельзя использовать из другого потока напрямую? Как правильно организовать сетевые операции в отдельном потоке?
  14. Как установить SSL/TLS-соединение с помощью QSslSocket?
  15. Какие недостатки имеет Qt по сравнению с альтернативами?
Основы QT

notes: Эта лекция знакомит с фреймворком Qt как основным инструментом для разработки кроссплатформенных сетевых приложений с GUI. Основной акцент — сетевые возможности Qt, поскольку предыдущие лекции уже рассмотрели низкоуровневые сокеты.

notes: Qt произносится как «кьют» (cute), не «ку-ти». Подчеркнуть, что Qt — не просто GUI-фреймворк, а полноценная платформа с модулями для сети, БД, мультимедиа. Для нашего курса ключевым является модуль Qt Network.

notes: Обратить внимание на ключевые вехи: Qt 4.7 (2010) — появление QML, Qt 5.0 (2012) — переход к QML как основному подходу, Qt 6.0 (2020) — переход на C++17. Актуальная версия на момент лекции — Qt 6.x.

notes: MOC — одна из ключевых особенностей Qt. Это препроцессор, который обрабатывает заголовочные файлы с макросом Q_OBJECT и генерирует дополнительный C++ код. Без MOC сигналы и слоты работать не будут. CMake с AUTOMOC ON автоматически вызывает MOC при сборке.

notes: Сигналы и слоты — центральный паттерн Qt, альтернатива классическим callback-функциям. Преимущества: типобезопасность, слабая связанность (loose coupling), автоматическое отключение при удалении объекта. Рекомендуется использовать новый синтаксис connect с указателями на функции (PMF), а не старый макросный SIGNAL/SLOT.

notes: Qt работает на событийном цикле (QEventLoop). События поступают от ОС (мышь, клавиатура, таймер) и от самой Qt (сигналы). Для сетевого программирования важно понимать, что данные от сокетов также обрабатываются через событийный цикл — readyRead это событие.

notes: Ключевой механизм Qt — родительско-дочерние отношения (parent-child). При удалении родителя автоматически удаляются все дочерние объекты. Это критически важно для GUI и сетевых объектов: при закрытии окна все виджеты и сокеты удаляются автоматически.

notes: Это ключевая секция для нашего курса. Qt Network абстрагирует платформенные различия между Winsock и BSD Sockets (рассмотренными в предыдущих лекциях). Все классы работают через асинхронный событийный цикл Qt — нет необходимости вручную использовать select/poll.

notes: Сравните с TCP-сервером на чистых сокетах из предыдущих лекций — код значительно короче. Обратить внимание на паттерн: newConnection -> nextPendingConnection -> readyRead. deleteLater — идиома Qt для безопасного удаления объектов в цикле событий.

notes: QUdpSocket позволяет отправлять и получать дейтаграммы без установления соединения. QNetworkDatagram (Qt 5.8+) — удобная обёртка, содержащая данные, адрес отправителя и порт. Подходит для широковещательной рассылки (broadcast) и multicast.

notes: QNetworkAccessManager — высокоуровневый API для HTTP/HTTPS. Автоматически управляет соединениями, поддерживает кэширование, cookies, прокси. Все запросы асинхронные — результат приходит через сигнал finished. Для JSON используйте QJsonDocument.

notes: Критически важная тема: QTcpSocket привязан к потоку, в котором создан (thread affinity). Нельзя создать сокет в главном потоке и использовать в другом. Правильный подход: создать сокет внутри метода воркера, который выполняется в рабочем потоке. QThread::isInterruptionRequested() — корректный способ остановить рабочий поток.

notes: QSslSocket наследует QTcpSocket и добавляет шифрование. Метод connectToHostEncrypted выполняет TCP-соединение + TLS-хэндшейк. Обязательно обрабатывать sslErrors — в продакшн-коде нельзя просто игнорировать ошибки сертификата. Qt использует OpenSSL (или платформенные TLS-библиотеки).

notes: Подвести итог: Qt предоставляет все уровни абстракции для сетевого программирования — от низкоуровневых TCP/UDP сокетов до высокоуровневых HTTP-запросов. Для курса «Программирование сетевых приложений» Qt — основной инструмент для создания кроссплатформенных GUI-клиентов и серверов.